/*
 * D-Bus Java Implementation Copyright (c) 2005-2006 Matthew Johnson This
 * program is free software; you can redistribute it and/or modify it under the
 * terms of either the GNU Lesser General Public License Version 2 or the
 * Academic Free Licence Version 2.1. Full licence texts are included in the
 * COPYING file with this program.
 */
package org.freedesktop.dbus;

import static org.freedesktop.dbus.Gettext._;

import java.lang.reflect.Array;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;

class ArrayFrob {
  static Hashtable<Class<? extends Object>, Class<? extends Object>> primitiveToWrapper = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
  static Hashtable<Class<? extends Object>, Class<? extends Object>> wrapperToPrimitive = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
  static {
    primitiveToWrapper.put(Boolean.TYPE, Boolean.class);
    primitiveToWrapper.put(Byte.TYPE, Byte.class);
    primitiveToWrapper.put(Short.TYPE, Short.class);
    primitiveToWrapper.put(Character.TYPE, Character.class);
    primitiveToWrapper.put(Integer.TYPE, Integer.class);
    primitiveToWrapper.put(Long.TYPE, Long.class);
    primitiveToWrapper.put(Float.TYPE, Float.class);
    primitiveToWrapper.put(Double.TYPE, Double.class);
    wrapperToPrimitive.put(Boolean.class, Boolean.TYPE);
    wrapperToPrimitive.put(Byte.class, Byte.TYPE);
    wrapperToPrimitive.put(Short.class, Short.TYPE);
    wrapperToPrimitive.put(Character.class, Character.TYPE);
    wrapperToPrimitive.put(Integer.class, Integer.TYPE);
    wrapperToPrimitive.put(Long.class, Long.TYPE);
    wrapperToPrimitive.put(Float.class, Float.TYPE);
    wrapperToPrimitive.put(Double.class, Double.TYPE);

  }

  @SuppressWarnings("unchecked")
  public static Object convert(Object o, Class<? extends Object> c) throws IllegalArgumentException {
    /*
     * Possible Conversions:* List<Integer> -> List<Integer>* List<Integer> ->
     * int[]* List<Integer> -> Integer[]* int[] -> int[]* int[] -> List<Integer>
     * * int[] -> Integer[]* Integer[] -> Integer[]* Integer[] -> int[]*
     * Integer[] -> List<Integer>
     */
    try {
      // List<Integer> -> List<Integer>
      if (List.class.equals(c) && (o instanceof List)) { return o; }

      // int[] -> List<Integer>
      // Integer[] -> List<Integer>
      if (List.class.equals(c) && o.getClass().isArray()) { return listify(o); }

      // int[] -> int[]
      // Integer[] -> Integer[]
      if (o.getClass().isArray() && c.isArray() && o.getClass().getComponentType().equals(c.getComponentType())) { return o; }

      // int[] -> Integer[]
      if (o.getClass().isArray() && c.isArray() && o.getClass().getComponentType().isPrimitive()) { return wrap(o); }

      // Integer[] -> int[]
      if (o.getClass().isArray() && c.isArray() && c.getComponentType().isPrimitive()) { return unwrap((Object[]) o); }

      // List<Integer> -> int[]
      if ((o instanceof List) && c.isArray() && c.getComponentType().isPrimitive()) { return delistprimitive((List<Object>) o,
          (Class<Object>) c.getComponentType()); }

      // List<Integer> -> Integer[]
      if ((o instanceof List) && c.isArray()) { return delist((List<Object>) o, (Class<Object>) c.getComponentType()); }

      if (o.getClass().isArray() && c.isArray()) { return type((Object[]) o, (Class<Object>) c.getComponentType()); }

    } catch (Exception e) {
      throw new IllegalArgumentException(e);
    }

    throw new IllegalArgumentException(MessageFormat.format(_("Not An Expected Convertion type from {0} to {1}"), new Object[] {
        o.getClass(), c }));
  }

  @SuppressWarnings("unchecked")
  public static <T> T[] delist(List<T> l, Class<T> c) throws IllegalArgumentException {
    return l.toArray((T[]) Array.newInstance(c, 0));
  }

  public static <T> Object delistprimitive(List<T> l, Class<T> c) throws IllegalArgumentException {
    Object o = Array.newInstance(c, l.size());
    for (int i = 0; i < l.size(); i++) {
      Array.set(o, i, l.get(i));
    }
    return o;
  }

  @SuppressWarnings("unchecked")
  public static <T> List<T> listify(Object o) throws IllegalArgumentException {
    if (o instanceof Object[]) { return listify((T[]) o); }
    if (!o.getClass().isArray()) { throw new IllegalArgumentException(_("Not an array")); }
    List<T> l = new ArrayList<T>(Array.getLength(o));
    for (int i = 0; i < Array.getLength(o); i++) {
      l.add((T) Array.get(o, i));
    }
    return l;
  }

  public static <T> List<T> listify(T[] ns) throws IllegalArgumentException {
    return Arrays.asList(ns);
  }

  public static Object[] type(Object[] old, Class<Object> c) {
    Object[] ns = (Object[]) Array.newInstance(c, old.length);
    for (int i = 0; i < ns.length; i++) {
      ns[i] = old[i];
    }
    return ns;
  }

  @SuppressWarnings("unchecked")
  public static <T> Object unwrap(T[] ns) throws IllegalArgumentException {
    Class<? extends T[]> ac = (Class<? extends T[]>) ns.getClass();
    Class<T> cc = (Class<T>) ac.getComponentType();
    Class<? extends Object> ncc = wrapperToPrimitive.get(cc);
    if (null == ncc) { throw new IllegalArgumentException(_("Not a wrapper type")); }
    Object o = Array.newInstance(ncc, ns.length);
    for (int i = 0; i < ns.length; i++) {
      Array.set(o, i, ns[i]);
    }
    return o;
  }

  @SuppressWarnings("unchecked")
  public static <T> T[] wrap(Object o) throws IllegalArgumentException {
    Class<? extends Object> ac = o.getClass();
    if (!ac.isArray()) { throw new IllegalArgumentException(_("Not an array")); }
    Class<? extends Object> cc = ac.getComponentType();
    Class<? extends Object> ncc = primitiveToWrapper.get(cc);
    if (null == ncc) { throw new IllegalArgumentException(_("Not a primitive type")); }
    T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o));
    for (int i = 0; i < ns.length; i++) {
      ns[i] = (T) Array.get(o, i);
    }
    return ns;
  }
}
